package top.lucky.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import top.lucky.common.DistrictPojo;
import top.lucky.common.EnumAddressKeyword;
import top.lucky.common.EnumAddressLevel;
import top.lucky.common.EnumAddressReplenish;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

@Slf4j
public enum AddressUtils {
    SINGLETON;

    // 正则表达式：过滤特殊字符
    private static final String ZH_CN_REG = "[^\\u4e00-\\u9fa5]+";
    private static final String SXQ = "市辖区";
    // 省级地址Set
    private static Set<String> provinceSet = new HashSet<>(34);
    // 市级地址Map
    private static Map<String, Object> cityMap = new HashMap<>(350);
    // 区级地址Map
    private static Map<String, Object> areaMap = new HashMap<>(3500);

    static {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(
                    AddressUtils.class.getResourceAsStream("/districts.txt")));
            //跳过第一行
            reader.readLine();
            String line = null;
            while ((line = reader.readLine()) != null) {
                String[] addressArray = line.split("\\s+");
                DistrictPojo pojo = new DistrictPojo(addressArray[0].intern(), addressArray[1].intern(), addressArray[2].intern(),
                        addressArray[3].intern(), addressArray[4].intern(), addressArray[5].intern(), null);
                /** 存储省级地址 */
                provinceSet.add(pojo.getProvinceName());

                /** 存储市级地址第一条，若存在相同的市级名称，则用列表形式存储 */
                if(cityMap.get(pojo.getCityName()) == null) {
                    cityMap.put(pojo.getCityName(), pojo);
                } else {
                    if(SXQ.equals(pojo.getCityName())
                            || EnumAddressKeyword.SZXXJXZQH.getKeyword().equals(pojo.getCityName())) {
                        Object cityObj = cityMap.get(pojo.getCityName());
                        if(cityObj instanceof DistrictPojo) {
                            DistrictPojo c_pojo = (DistrictPojo) cityObj;
                            if(!c_pojo.getProvinceName().equals(pojo.getProvinceName())) {
                                // 不同省份
                                List<DistrictPojo> cityList = new ArrayList<>();
                                cityList.add(c_pojo);
                                cityList.add(pojo);
                                cityMap.remove(pojo.getCityName());
                                cityMap.put(pojo.getCityName(), cityList);
                            }
                        } else {
                            List<DistrictPojo> c_list = (List<DistrictPojo>) cityMap.get(pojo.getCityName());
                            boolean sameProvinceFlag = false;
                            for(DistrictPojo a : c_list) {
                                if(a.getProvinceName().equals(pojo.getProvinceName())) {
                                    sameProvinceFlag = true;
                                    break;
                                }
                            }
                            if(!sameProvinceFlag) {
                                // 没有相同省份
                                c_list.add(pojo);
                            }
                        }
                    }
                }
                /** 存储区级地址第一条，若存在相同的区级名称，则用列表形式存储 */
                if(areaMap.get(pojo.getAreaName()) == null) {
                    areaMap.put(pojo.getAreaName(), pojo);
                } else {
                    Object areaObj = areaMap.get(pojo.getAreaName());
                    if(areaObj instanceof DistrictPojo) {
                        List<DistrictPojo> areaList = new ArrayList<>();
                        areaList.add((DistrictPojo) areaObj);
                        areaList.add(pojo);
                        areaMap.remove(pojo.getAreaName());
                        areaMap.put(pojo.getAreaName(), areaList);
                    } else {
                        ((List<DistrictPojo>) areaObj).add(pojo);
                    }
                }
            }
        } catch (IOException e) {
            log.error("读取districts.txt文件异常", e);
        } finally {
            log.info("读取districts.txt完成");
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error("关闭文件流异常", e);
                }
            }
        }
    }

    /***
     * 解析地址
     *
     * @param address
     * @return com.hsjry.lang.common.pojo.DistrictPojo
     * @author yeshuai
     * @date 2021/11/24 11:18
     */
    public DistrictPojo resolve(String address) {
        if(StringUtils.isEmpty(address)) {
            log.info("解析结果：地址为空，无法解析");
            return null;
        }
        DistrictPojo pojo = new DistrictPojo();
        Map<EnumAddressLevel, String> addressCutMap = new HashMap<>();
        // 切割地址
        String leftAddress = cutAddress(address, addressCutMap);
        // 匹配地址
        matchAddress(pojo, leftAddress, addressCutMap);
        if(pojo.getProvinceName() == null) {
            log.info("解析结果：无法解析");
            return null;
        } else {
            log.info("解析结果：{}", pojo.toString());
            return pojo;
        }
    }


    /***
     * 切割地址
     *
     * @param address
     * @param addressCutMap
     * @return java.lang.String
     * @author yeshuai
     * @date 2021/11/24 15:07
     */
    private String cutAddress(String address, Map<EnumAddressLevel, String> addressCutMap) {
        log.info("地址输入：{}", address);
        // 省级 切割的地址
        String provinceCutAddr = null;
        // 市级 切割的地址
        String cityCutAddr = null;
        // 区级 切割的地址
        String areaCutAddr = null;
        for (EnumAddressKeyword e : EnumAddressKeyword.values()) {
            int index = address.indexOf(e.getKeyword());
            if(index != -1) {
                // 若区级地址已切割出有效地址，则跳出循环
                if(areaCutAddr != null) {
                    break;
                }
                String cutAddress = null;
                if(e.getHasPrefix()) {
                    cutAddress = address.substring(0, index + e.getLength());
                    address = address.substring(index + e.getLength());
                } else {
                    cutAddress = address.substring(index, index + e.getLength());
                    address = address.substring(index + e.getLength());
                }
                // 因不存在 %区%市 的地址，故切割出来的此类地址直接跳过本轮循环
                if(cutAddress.contains(EnumAddressKeyword.QU.getKeyword())
                        && cutAddress.contains(EnumAddressKeyword.XJSHI.getKeyword())
                        && cutAddress.indexOf(EnumAddressKeyword.QU.getKeyword()) <
                        cutAddress.indexOf(EnumAddressKeyword.XJSHI.getKeyword())) {
                    address = cutAddress + address;
                    continue;
                }

                // 因不存在 %区%地区 的地址，故切割出来的此类地址直接跳过本轮循环
                if(cutAddress.contains(EnumAddressKeyword.QU.getKeyword())
                        && cutAddress.contains(EnumAddressKeyword.DIQU.getKeyword())
                        && cutAddress.indexOf(EnumAddressKeyword.QU.getKeyword()) <
                        cutAddress.indexOf(EnumAddressKeyword.DIQU.getKeyword())) {
                    address = cutAddress + address;
                    continue;
                }

                // 因除了辉县市以外，不存在 %县%市 的地址，故切割出来的此类地址直接跳过本轮循环
                if(cutAddress.contains(EnumAddressKeyword.XIAN.getKeyword())
                        && cutAddress.contains(EnumAddressKeyword.XJSHI.getKeyword())
                        && cutAddress.indexOf(EnumAddressKeyword.XIAN.getKeyword()) <
                        cutAddress.indexOf(EnumAddressKeyword.XJSHI.getKeyword())
                        && !cutAddress.equals("辉县市")) {
                    address = cutAddress + address;
                    continue;
                }

                // 因除了梅县区以外，不存在 %县%区 的地址，故切割出来的此类地址直接跳过本轮循环
                if(cutAddress.contains(EnumAddressKeyword.XIAN.getKeyword())
                        && cutAddress.contains(EnumAddressKeyword.QU.getKeyword())
                        && cutAddress.indexOf(EnumAddressKeyword.XIAN.getKeyword()) <
                        cutAddress.indexOf(EnumAddressKeyword.QU.getKeyword())
                        && !cutAddress.equals("梅县区")) {
                    address = cutAddress + address;
                    continue;
                }

                cutAddress = cutAddress.replaceAll(ZH_CN_REG, "");
                // 地址补全
                EnumAddressReplenish enumAddressReplenish = EnumAddressReplenish.find(cutAddress);
                if(enumAddressReplenish != null) {
                    cutAddress = enumAddressReplenish.getCompleteAddress();
                }
                if (EnumAddressLevel.PROVINCE_LEVEL.equals(e.getEnumAddressLevel())) {
                    if(provinceSet.contains(cutAddress)) {
                        if(provinceCutAddr == null) {
                            provinceCutAddr = cutAddress;
                        }
                    }
                } else if (EnumAddressLevel.CITY_LEVEL.equals(e.getEnumAddressLevel())) {
                    if(cityMap.get(cutAddress) != null) {
                        if(cityCutAddr == null) {
                            cityCutAddr = cutAddress;
                        }
                    } else {
                        if(areaMap.get(cutAddress) != null) {
                            if(areaCutAddr == null) {
                                areaCutAddr = cutAddress;
                            }
                        }
                    }
                } else {
                    if(areaMap.get(cutAddress) != null) {
                        if(areaCutAddr == null) {
                            areaCutAddr = cutAddress;
                        }
                    }
                }
            }
        }
        String validCutAddr = "";
        if(provinceCutAddr != null) {
            validCutAddr = validCutAddr + provinceCutAddr + ", ";
            addressCutMap.put(EnumAddressLevel.PROVINCE_LEVEL, provinceCutAddr);
        }
        if(cityCutAddr != null) {
            validCutAddr = validCutAddr + cityCutAddr + ", ";
            addressCutMap.put(EnumAddressLevel.CITY_LEVEL, cityCutAddr);
        }
        if(areaCutAddr != null) {
            validCutAddr = validCutAddr + areaCutAddr + ", ";
            addressCutMap.put(EnumAddressLevel.AREA_LEVEL, areaCutAddr);
        }
        // 打印有效切割地址
        if(!validCutAddr.equals("")) {
            log.info("有效切割地址：{}", validCutAddr.substring(0, validCutAddr.length() - 2));
        }
        return address;
    }

    /***
     * 匹配有效地址
     *
     * @param pojo
     * @param leftAddress
     * @param addressCutMap
     * @return void
     * @author yeshuai
     * @date 2021/11/24 16:31
     */
    private void matchAddress(DistrictPojo pojo, String leftAddress, Map<EnumAddressLevel, String> addressCutMap){
        // 省级 切割的有效地址
        String province = addressCutMap.get(EnumAddressLevel.PROVINCE_LEVEL);
        // 市级 切割的有效地址
        String city = addressCutMap.get(EnumAddressLevel.CITY_LEVEL);
        // 区级 切割的有效地址
        String area = addressCutMap.get(EnumAddressLevel.AREA_LEVEL);
        // 省空
        if(province == null) {
            // 市不空 区空
            if(city != null && area == null) {
                Object obj = cityMap.get(city);
                if(obj instanceof DistrictPojo) {
                    assignment((DistrictPojo) obj, pojo, leftAddress);
                }
            } else if(city == null && area != null) {
                // 市空 区不空
                Object obj = areaMap.get(area);
                if(obj instanceof DistrictPojo) {
                    assignment((DistrictPojo) obj, pojo, leftAddress);
                }
            } else if(city != null && area != null) {
                // 市不空 区不空
                Object areaObj = areaMap.get(area);
                if(areaObj instanceof DistrictPojo) {
                    if(((DistrictPojo) areaObj).getCityName().equals(city)) {
                        assignment((DistrictPojo) areaObj, pojo, leftAddress);
                    }
                } else {
                    List<DistrictPojo> list = (List<DistrictPojo>) areaObj;
                    for(DistrictPojo a : list) {
                        if(a.getCityName().equals(city)) {
                            assignment(a, pojo, leftAddress);
                            break;
                        }
                    }
                }
            }
        } else {
            // 省不空
            if(city != null && area == null) {
                // 市不空 区空
                Object cityObj = cityMap.get(city);
                if(cityObj instanceof DistrictPojo) {
                    if(((DistrictPojo) cityObj).getProvinceName().equals(province)) {
                        assignment((DistrictPojo) cityObj, pojo, leftAddress);
                    }
                } else {
                    List<DistrictPojo> list = (List<DistrictPojo>) cityObj;
                    for(DistrictPojo a : list) {
                        if(a.getProvinceName().equals(province)) {
                            assignment(a, pojo, leftAddress);
                            break;
                        }
                    }
                }
            } else if(city == null && area != null) {
                // 市空 区不空
                Object areaObj = areaMap.get(area);
                if(areaObj instanceof DistrictPojo) {
                    if(((DistrictPojo) areaObj).getProvinceName().equals(province)) {
                        assignment((DistrictPojo) areaObj, pojo, leftAddress);
                    }
                } else {
                    List<DistrictPojo> list = (List<DistrictPojo>) areaObj;
                    for(DistrictPojo a : list) {
                        if(a.getProvinceName().equals(province)) {
                            assignment(a, pojo, leftAddress);
                            break;
                        }
                    }
                }
            } else if(city != null && area != null) {
                // 市不空 区不空
                /** 以区级地址为出发点 */
                Object areaObj = areaMap.get(area);
                if(areaObj instanceof DistrictPojo) {
                    if(((DistrictPojo) areaObj).getCityName().equals(city) ||
                            ((DistrictPojo) areaObj).getProvinceName().equals(province)) {
                        assignment((DistrictPojo) areaObj, pojo, leftAddress);
                    }
                } else {
                    List<DistrictPojo> list = (List<DistrictPojo>) areaObj;
                    for(DistrictPojo a : list) {
                        if(a.getCityName().equals(city) || a.getProvinceName().equals(province)) {
                            assignment(a, pojo, leftAddress);
                            break;
                        }
                    }
                }

                /** 若以区级地址为出发点没解析出地址，则以市级地址为出发点进行再次解析 */
                if(pojo.getProvinceName() == null) {
                    Object cityObj = cityMap.get(city);
                    if(cityObj instanceof DistrictPojo) {
                        if(((DistrictPojo) cityObj).getProvinceName().equals(province)) {
                            assignment((DistrictPojo) cityObj, pojo, leftAddress);
                        }
                    } else {
                        List<DistrictPojo> list = (List<DistrictPojo>) cityObj;
                        for(DistrictPojo a : list) {
                            if(a.getProvinceName().equals(province)) {
                                assignment(a, pojo, leftAddress);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    /***
     * 赋值
     *
     * @param source
     * @param target
     * @param leftAddress
     * @return void
     * @author yeshuai
     * @date 2021/11/25 11:07
     */
    private void assignment(DistrictPojo source, DistrictPojo target, String leftAddress) {
        target.setProvinceCode(source.getProvinceCode());
        target.setProvinceName(source.getProvinceName());
        target.setCityCode(source.getCityCode());
        target.setCityName(source.getCityName());
        target.setAreaCode(source.getAreaCode());
        target.setAreaName(source.getAreaName());
        target.setAddress(target.getProvinceName() + target.getCityName() + target.getAreaName() + leftAddress);
    }
}
